import cv2 as cv
import numpy as np
from skimage import morphology
import math as m
from numba import jit  # 转换为机器代码，加速运算
from matplotlib import pyplot as plt
from PyQt5.QtWidgets import *
from Experiment5.Conv5_UI import Ui_Dialog as UI_Conv
import time

def morphy_binary(img, deal_Type):
    """根据用户的选择，对于图像做相应的二值形态学处理"""
    if img.shape[-1] == 3:
        pass
    q_dialog = QDialog()
    dlg = UI_Conv()
    dlg.setupUi(q_dialog)  # 继承QDialog()， 使得dialog具有show()方法
    q_dialog.show()
    if q_dialog.exec() == QDialog.Accepted:  # 提取用户交互的参数
        np_kernel = np.array(
            [[int(dlg.lineEdit1.text()), int(dlg.lineEdit2.text()), int(dlg.lineEdit3.text()), int(dlg.lineEdit4.text()), int(dlg.lineEdit5.text())],
             [int(dlg.lineEdit6.text()), int(dlg.lineEdit7.text()), int(dlg.lineEdit8.text()), int(dlg.lineEdit9.text()), int(dlg.lineEdit10.text())],
             [int(dlg.lineEdit11.text()), int(dlg.lineEdit12.text()), int(dlg.lineEdit13.text()), int(dlg.lineEdit14.text()), int(dlg.lineEdit15.text())],
             [int(dlg.lineEdit16.text()), int(dlg.lineEdit17.text()), int(dlg.lineEdit18.text()), int(dlg.lineEdit19.text()), int(dlg.lineEdit20.text())],
             [int(dlg.lineEdit21.text()), int(dlg.lineEdit22.text()), int(dlg.lineEdit23.text()), int(dlg.lineEdit24.text()), int(dlg.lineEdit25.text())]
            ])

        if deal_Type == 1:
            img = erosion_binary(img, np_kernel)
        elif deal_Type == 2:
            img = dilation_binary(img, np_kernel)
        elif deal_Type == 3:
            img = open_binary(img, np_kernel)
        elif deal_Type == 4:
            img = close_binary(img, np_kernel)
    return img

def morphy_gray(img, deal_Type):
    """根据用户的选择，对于图像做相应的灰值形态学处理"""
    if img.shape[-1] == 3:
        pass
    q_dialog = QDialog()
    dlg = UI_Conv()
    dlg.setupUi(q_dialog)  # 继承QDialog()， 使得dialog具有show()方法
    q_dialog.show()
    if q_dialog.exec() == QDialog.Accepted:  # 提取用户交互的参数
        np_kernel = np.array(
            [[int(dlg.lineEdit1.text()), int(dlg.lineEdit2.text()), int(dlg.lineEdit3.text()), int(dlg.lineEdit4.text()), int(dlg.lineEdit5.text())],
             [int(dlg.lineEdit6.text()), int(dlg.lineEdit7.text()), int(dlg.lineEdit8.text()), int(dlg.lineEdit9.text()), int(dlg.lineEdit10.text())],
             [int(dlg.lineEdit11.text()), int(dlg.lineEdit12.text()), int(dlg.lineEdit13.text()), int(dlg.lineEdit14.text()), int(dlg.lineEdit15.text())],
             [int(dlg.lineEdit16.text()), int(dlg.lineEdit17.text()), int(dlg.lineEdit18.text()), int(dlg.lineEdit19.text()), int(dlg.lineEdit20.text())],
             [int(dlg.lineEdit21.text()), int(dlg.lineEdit22.text()), int(dlg.lineEdit23.text()), int(dlg.lineEdit24.text()), int(dlg.lineEdit25.text())]
            ])

        if deal_Type == 1:
            img = erosion_gray(img, np_kernel)
        elif deal_Type == 2:
            img = dilation_gray(img, np_kernel)
        elif deal_Type == 3:
            img = open_gray(img, np_kernel)
        elif deal_Type == 4:
            img = close_gray(img, np_kernel)
        elif deal_Type == 5:
            img = morphy_gray_edge(img, np_kernel)
    return img

def erosion_binary(img, np_kernel):
    """*功能 : 根据传入的图像进行二值腐蚀，默认255像素点为目标
    *注意，传入的图像必须为二值图像, kernel为结构形状, 函数：XΘS={x|S+x∈X}"""
    time1 = time.time()  # 程序计时开始
    rows, cols = img.shape[:2]  # 获取宽和高
    new_img = np.zeros(img.shape, dtype=np.uint8)  # 新建同原图大小一致的空图像
    # 手写二值图像腐蚀过程
    
    #此处写入你的代码
    # 手写二值图像腐蚀过程
    k_rows, k_cols = np_kernel.shape
    k_center = (k_rows // 2, k_cols // 2)  # 结构元素中心坐标
    for i in range(rows):
        for j in range(cols):
            valid = True
            # 遍历结构元素
            for x in range(k_rows):
                for y in range(k_cols):
                    if np_kernel[x, y] != 0:  # 结构元素有效位置
                        # 计算对应图像坐标
                        img_x = i + x - k_center[0]
                        img_y = j + y - k_center[1]
                        # 边界检查
                        if img_x < 0 or img_x >= rows or img_y < 0 or img_y >= cols:
                            valid = False
                            break
                        # 检查是否为目标像素
                        if img[img_x, img_y] != 255:
                            valid = False
                            break
                if not valid:
                    break
            if valid:
                new_img[i, j] = 255
            else:
                new_img[i, j] = 0
    time2 = time.time()  # 程序计时结束
    print("手写二值腐蚀运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))


    # opencv图像腐蚀过程
    #此处写入你的代码
    time1 = time.time()
    cv_ero = cv.erode(img, np_kernel.astype(np.uint8), iterations=1)
    time2 = time.time()  # 程序计时结束
    print("opencv二值腐蚀运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img
    # return cv_ero

def dilation_binary(img, np_kernel):
    """*功能 : 根据传入的图像进行二值膨胀，默认255像素点为目标
    *注意，传入的图像必须为二值图像, kernel为结构形状, 这里用的是腐蚀函数的对偶运算：
    X⊕S=∪{S+x|x∈X}=(X^c Θ S^v)^c"""
    time1 = time.time()  # 程序计时开始
    # 手写图像膨胀过程

    #此处写入你的代码
    # 步骤1：计算输入图像X的补集 X^c（目标与背景反转）
    X_complement = 255 - img  # 二值图像中0↔255

    # 步骤2：计算结构元素S的转置 Ŝ（旋转180°）
    S_hat = np.rot90(np_kernel, 2)  # 旋转180°等价于两次90°旋转

    # 步骤3：对补集图像用转置后的结构元素进行腐蚀：X^c Θ Ŝ
    eroded = erosion_binary(X_complement, S_hat)

    # 步骤4：对腐蚀结果取补集，得到膨胀结果：(X^c Θ Ŝ)^c = X⊕S
    new_img = 255 - eroded
    time2 = time.time()  # 程序计时结束
    print("手写二值膨胀运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))

    # opencv图像膨胀过程
    #此处写入你的代码
    time1 = time.time()
    cv_dil = cv.dilate(img, np_kernel.astype(np.uint8), iterations=1)
    time2 = time.time()  # 程序计时结束
    print("opencv二值膨胀运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img

def open_binary(img, np_kernel):
    """*功能 : 根据传入的图像进行二值开，默认255像素点为目标(ww)
    *注意，传入的图像必须为二值图像，二值开函数：X○S=(XΘS)⊕S"""
    time1 = time.time()  # 程序计时开始
    # 手写图像二值开运算过程
    new_img = erosion_binary(img, np_kernel)  # 先腐蚀
    new_img = dilation_binary(new_img, np_kernel)  # 再膨胀
    time2 = time.time()  # 程序计时结束
    print("手写二值开运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))

    # opencv图像二值开运算过程
    new_img_cv = cv.morphologyEx(img, cv.MORPH_OPEN, np_kernel.astype(np.uint8))
    time2 = time.time()  # 程序计时结束
    print("opencv二值开运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img

def close_binary(img, np_kernel):
    """*功能 : 根据传入的图像进行二值闭，默认255像素点为目标(ww)
        *注意，传入的图像必须为二值图像，二值闭函数：X·S=(X⊕S)ΘS"""
    time1 = time.time()  # 程序计时开始
    # 手写图像二值闭运算过程
    new_img = dilation_binary(img, np_kernel)  # 先膨胀
    new_img = erosion_binary(new_img, np_kernel) # 再腐蚀
    time2 = time.time()  # 程序计时结束
    print("手写二值闭运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))

    # opencv图像二值闭运算过程
    new_img = cv.morphologyEx(img, cv.MORPH_CLOSE, np_kernel.astype(np.uint8))
    time2 = time.time()  # 程序计时结束
    print("opencv二值闭运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img

def fast_thin(img, deal_Type):
    """*功能 : 根据传入的图像进行快速形态学细化，默认0像素点为背景"""
    time1 = time.time()  # 程序计时开始
    
    #此处写入你的代码
    # 1. 预处理：确保输入为二值图像（目标255，背景0）
    # 若输入为彩色图，先转为灰度图
    if len(img.shape) == 3 and img.shape[-1] == 3:
        img = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

    # 阈值处理：将灰度图转为二值图（大于200视为背景，否则视为目标）
    ret, binary = cv.threshold(img, 200, 255, cv.THRESH_BINARY)
    # 转换为skimage所需的布尔矩阵（True表示目标，符合骨架提取函数输入要求）
    binary_bool = (binary == 255).astype(bool)

    # 2. 根据deal_Type选择骨架提取算法
    if deal_Type == 1:
        # 方法1：skimage.skeletonize（经典细化算法）
        skeleton = morphology.skeletonize(binary_bool)
        method_name = "skimage skeletonize"
    elif deal_Type == 2:
        # 方法2：skimage.medial_axis（中轴变换）
        skeleton, _ = morphology.medial_axis(binary_bool, return_distance=True)  # 忽略距离图
        method_name = "skimage medial_axis"
    else:
        # 处理无效的deal_Type
        raise ValueError("deal_Type参数错误：1-skeletonize，2-medial_axis")

    # 3. 结果转换：将布尔骨架转为uint8二值图（255表示骨架，0表示背景）
    new_img = (skeleton * 255).astype(np.uint8)
    time2 = time.time()  # 程序计时结束
    print("opencv细化算法算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img

def erosion_gray(img, np_kernel):
    """*功能 : 根据传入的图像进行灰值腐蚀*注意，传入的图像必须为灰值图像,
       kernel为结构形状, 函数：(fΘb)(s,t)=min{f(s+x, t+y)}"""
    time1 = time.time()  # 程序计时开始
    rows, cols = img.shape[:2]  # 获取宽和高
    new_img = np.zeros(img.shape, dtype=np.uint8)  # 新建同原图大小一致的空图像

    
    #此处写入你的代码
    # 手写灰值图像腐蚀过程
    k_rows, k_cols = np_kernel.shape
    k_center = (k_rows // 2, k_cols // 2)
    for i in range(rows):
        for j in range(cols):
            min_val = 255  # 初始化最小值为最大灰度值
            for x in range(k_rows):
                for y in range(k_cols):
                    if np_kernel[x, y] != 0:
                        img_x = i + x - k_center[0]
                        img_y = j + y - k_center[1]
                        if 0 <= img_x < rows and 0 <= img_y < cols:
                            if img[img_x, img_y] < min_val:
                                min_val = img[img_x, img_y]
            new_img[i, j] = min_val
    time2 = time.time()  # 程序计时结束
    print("手写灰值腐蚀运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))

    # opencv图像腐蚀过程
    #此处写入你的代码
    time1= time.time()
    cv_ero = cv.erode(img, np_kernel.astype(np.uint8), iterations=1)
    time2 = time.time()  # 程序计时结束
    print("opencv灰值腐蚀运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img

def dilation_gray(img, np_kernel):
    """*功能 : 根据传入的图像进行灰值膨胀*注意，传入的图像必须为灰值图像,
       kernel为结构形状, 函数：(fΘb)(s,t)=min{f(s+x, t+y)}"""
    time1 = time.time()  # 程序计时开始
    rows, cols = img.shape[:2]  # 获取宽和高
    new_img = np.zeros(img.shape, dtype=np.uint8)  # 新建同原图大小一致的空图像
    # 手写灰值图像膨胀过程
    
   #此处写入你的代码
    k_rows, k_cols = np_kernel.shape
    k_center = (k_rows // 2, k_cols // 2)
    for i in range(rows):
        for j in range(cols):
            max_val = 0  # 初始化最大值为最小灰度值
            for x in range(k_rows):
                for y in range(k_cols):
                    if np_kernel[x, y] != 0:
                        img_x = i + x - k_center[0]
                        img_y = j + y - k_center[1]
                        if 0 <= img_x < rows and 0 <= img_y < cols:
                            if img[img_x, img_y] > max_val:
                                max_val = img[img_x, img_y]
            new_img[i, j] = max_val
    time2 = time.time()  # 程序计时结束
    print("手写灰值膨胀运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))

    # opencv图像腐蚀过程
    #此处写入你的代码
    time1 = time.time()
    cv_dil = cv.dilate(img, np_kernel.astype(np.uint8), iterations=1)
    time2 = time.time()  # 程序计时结束
    print("opencv灰值膨胀运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img

def open_gray(img, np_kernel):
    """*功能 : 根据传入的图像进行灰值开，灰值开函数：X○S=(XΘS)⊕S"""
    time1 = time.time()  # 程序计时开始
    # 手写图像灰值开运算过程
    new_img = erosion_gray(img, np_kernel) # 先腐蚀
    new_img = dilation_gray(new_img, np_kernel)  # 再膨胀
    time2 = time.time()  # 程序计时结束
    print("手写灰值开运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))

    # opencv图像灰值开运算过程
    new_img_cv = cv.morphologyEx(img, cv.MORPH_OPEN, np_kernel.astype(np.uint8))
    time2 = time.time()  # 程序计时结束
    print("opencv灰值开运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img

def close_gray(img, np_kernel):
    """*功能 : 根据传入的图像进行灰值闭，灰值闭运算函数：X·S=(X⊕S)ΘS"""
    time1 = time.time()  # 程序计时开始
    # 手写图像灰值闭运算过程
    new_img = dilation_gray(img, np_kernel)  # 先膨胀
    new_img = erosion_gray(new_img, np_kernel) # 再腐蚀
    time2 = time.time()  # 程序计时结束
    print("手写灰值闭运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))

    # opencv图像灰值闭运算过程
    new_img_cv = cv.morphologyEx(img, cv.MORPH_CLOSE, np_kernel.astype(np.uint8))
    time2 = time.time()  # 程序计时结束
    print("opencv灰值闭运算程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img

def morphy_gray_edge(img, np_kernel):
    """*功能 : 根据传入的图像进行灰值边缘求取，函数为：g(X)=(X⊕S)-(XΘS)"""
    time1 = time.time()  # 程序计时开始
    # 手写图像灰值形态学边缘求取过程
    ero_img = erosion_gray(img, np_kernel)  # 腐蚀
    dil_img = dilation_gray(img, np_kernel)  # 膨胀
    new_img = cv.absdiff(dil_img, ero_img)
    time2 = time.time()  # 程序计时结束
    print("手写灰值形态学边缘程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))

    # opencv图像灰值形态学边缘求取过程
    time1 = time.time()
    ero_img = cv.erode(img, np_kernel.astype(np.uint8))  # 腐蚀
    dil_img = cv.dilate(img, np_kernel.astype(np.uint8))  # 膨胀
    new_img_cv   = cv.absdiff(dil_img, ero_img)
    time2 = time.time()  # 程序计时结束
    print("opencv灰值形态学边缘程序处理时间：%.3f毫秒" % ((time2 - time1) * 1000))
    return new_img